#pragma once

#include <cstring>
#include <cerrno>
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <strings.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <functional>
#include "Log.hpp"
#include "InetAddr.hpp"

#define BUF_SIZE 64

enum
{
    SOCKET_ERROR = 1,
    BIND_ERROR,
    USAGE_ERROR
};


const static int default_fd = -1;
using func_t = std::function<std::string(const std::string&, bool&)>; // 定义一个函数类型

class UdpServer
{
public:
    UdpServer(uint16_t port, func_t func)
        :_sockfd(default_fd), _port(port), _isRunning(false), _func(func)
    {}

    void InitServer()
    {
        // 1. 创建UDP套接字(文件信息--栈上)
        int sfd = socket(AF_INET, SOCK_DGRAM, 0);
        if(sfd < 0)
        {
            LOG(FATAL, "socket error, %s, %d", strerror(errno), errno);
            exit(SOCKET_ERROR);
        }
        LOG(INFO, "socket success, sfd: %d", sfd);
        _sockfd = sfd;

        // 2. bind

        // 2.0 填充sockaddr_in结构（网络信息--栈上）
        struct sockaddr_in local;
        bzero(&local, sizeof(struct sockaddr_in));
        local.sin_family = AF_INET;
        // port 要经过网络传送给对方，所以端口号要进行 主机序列 -> 网络序列，端口号为2个字节，s
        local.sin_port = htons(_port); 
        // a.将点分十进制字符串转换为4字节整数
        // b.将主机序列转换为网络序列
        // in_addr_t inet_addr(const char *cp); 可以完成a、b两个工作

        local.sin_addr.s_addr = INADDR_ANY; // 不绑定特定IP，并且不需要htonl，因为0没有大小端问题

        // 2.1 绑定到内核中
        int n = bind(_sockfd, (struct sockaddr*)&local, sizeof(struct sockaddr_in));
        if(n < 0)
        {
            LOG(FATAL, "bind error, %s : %d", strerror(errno), errno);
            exit(BIND_ERROR);
        }
        LOG(INFO, "bind success");
    }

    void Start()
    {
        // 死循环式地启动服务器，直到管理者不想运行了/出错了
        _isRunning = true;
        LOG(DEBUG, "server is running...");
        
        char buffer[BUF_SIZE];
        ssize_t numBytes = 0;
        struct sockaddr_in CLaddr;
        socklen_t len = sizeof(CLaddr);

        while(true)
        {
            // 1. server 接受来自客户端的信息
            numBytes = recvfrom(_sockfd, buffer, BUF_SIZE - 1, 0, (struct sockaddr*)&CLaddr, &len);
            if(numBytes == -1)
            {
                LOG(ERROR, "recvform failed");
            }
            buffer[numBytes] = 0;

            // 获取向服务器端发送消息 客户端 的IP和port
            InetAddr addr(CLaddr);
            LOG(INFO, "get message: [%s] from %s <%d>", buffer, addr.IP().c_str(), addr.Port());


            // 处理由客户端的信息
            bool trust = false;
            std::string response = _func(buffer, trust);
            response = response + "(flag:" + std::to_string(trust) + ")";

            // 2. 将处理后的信息发送到客户端
            if(sendto(_sockfd, response.c_str(), response.size(), 0, (struct sockaddr*)&CLaddr, len) != numBytes)
                LOG(ERROR, "sendto");
        }
        _isRunning = false;
        LOG(DEBUG, "server is closed...");
    }

    ~UdpServer()
    {}

private:
    int _sockfd;
    // std::string _ip;
    uint16_t _port; // 服务器使用的端口号
    bool _isRunning; // 标识服务器是否在运行中
    func_t _func; // 服务器处理信息的方法
};